using System;
using System.Collections.Generic;

namespace ZFramework.Runtime.DataCenter
{
    public sealed partial class DataCenterManager : FrameworkManagerBase
    {
        private Dictionary<Type, DataInfo> _dicDataInfos;
        private FrameworkLinkedList<DataInfo> _linkedListDataInfos;

        protected override void Awake()
        {
            base.Awake();
            _dicDataInfos = new Dictionary<Type, DataInfo>();
            _linkedListDataInfos = new FrameworkLinkedList<DataInfo>();
        }

        void Start()
        {
            
        }

        public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {

        }

        public override void Shutdown()
        {
            for (LinkedListNode<DataInfo> current = _linkedListDataInfos.Last;
                current != null;
                current = current.Previous)
            {
                current.Value.DataCenter.Shutdown();
            }

            _dicDataInfos.Clear();
            _linkedListDataInfos.Clear();
        }

        public void AddData<T>() where T : DataCenter
        {
            AddData<T>(Constant.DefaultPriority);
        }

        public void AddData<T>(int priority) where T : DataCenter
        {
            Type type = typeof(T);

            if (_dicDataInfos.ContainsKey(type))
            {
                throw new Exception(Utility.Text.Format("Data Type '{0}' is already exist.", type.ToString()));
            }

            DataCenter dataCenter = (DataCenter) Activator.CreateInstance(type);
            if (dataCenter == null)
            {
                throw new Exception(Utility.Text.Format("Can not create data '{0}'.", type.FullName));
            }

            AddData(dataCenter, type, priority);
        }

        public void AddData(DataCenter dataCenter)
        {
            AddData(dataCenter, Constant.DefaultPriority);
        }

        public void AddData(DataCenter dataCenter, int priority)
        {
            if (dataCenter == null)
            {
                throw new Exception("Can not add null data");
            }

            AddData(dataCenter, dataCenter.GetType(), priority);
        }

        private void AddData(DataCenter dataCenter, Type dataType, int priority)
        {
            if (dataCenter == null)
            {
                throw new Exception("Can not add null data");
            }

            DataInfo dataInfo = DataInfo.Create(dataCenter);
            dataInfo.Priority = priority;

            _dicDataInfos.Add(dataType, dataInfo);

            LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
            while (current != null)
            {
                if (dataInfo.Priority > current.Value.Priority)
                {
                    break;
                }

                current = current.Next;
            }

            if (current != null)
            {
                _linkedListDataInfos.AddBefore(current, dataInfo);
            }
            else
            {
                _linkedListDataInfos.AddLast(dataInfo);
            }
        }

        public void RemoveData<T>() where T : DataCenter
        {
            Type type = typeof(T);

            DataInfo dataInfo = null;

            if (!_dicDataInfos.TryGetValue(type, out dataInfo))
            {
                throw new Exception(Utility.Text.Format("Data Type '{0}' is not exist.", type.ToString()));
            }

            dataInfo.DataCenter.Unload();
            dataInfo.DataCenter.Shutdown();

            _dicDataInfos.Remove(type);
        }

        public void RemoveData(DataCenter dataCenter)
        {
            if (dataCenter == null)
            {
                throw new Exception(Utility.Text.Format("Data '{0}' is null.", dataCenter.Name.ToString()));
            }

            Type type = dataCenter.GetType();
            DataInfo dataInfo = null;

            if (!_dicDataInfos.TryGetValue(type, out dataInfo))
            {
                throw new Exception(Utility.Text.Format("Data Type '{0}' is not exist.", type.ToString()));
            }

            if (!ReferenceEquals(dataInfo.DataCenter, dataCenter))
            {
                throw new Exception(Utility.Text.Format("Data '{0}' is not the same instance.", type.ToString()));
            }

            dataInfo.DataCenter.Unload();
            dataInfo.DataCenter.Shutdown();

            _dicDataInfos.Remove(type);
        }

        public DataCenter[] GetAllData()
        {
            if (_dicDataInfos != null)
            {
                DataCenter[] Datas = new DataCenter[_linkedListDataInfos.Count];


                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                int index = 0;
                while (current != null)
                {
                    Datas[index] = current.Value.DataCenter;
                    index++;
                    current = current.Next;
                }

                return Datas;
            }

            return null;
        }

        public void GetAllData(List<DataCenter> result)
        {
            if (result == null)
            {
                throw new Exception("Results is invalid.");
            }

            result.Clear();

            if (_linkedListDataInfos != null)
            {
                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                while (current != null)
                {
                    result.Add(current.Value.DataCenter);
                    current = current.Next;
                }
            }
        }

        public T GetData<T>() where T : DataCenter
        {
            Type type = typeof(T);

            DataInfo dataInfo = null;

            if (!_dicDataInfos.TryGetValue(type, out dataInfo))
            {
                throw new Exception(Utility.Text.Format("Data Type '{0}' is not exist.", type.ToString()));
            }

            return (T) dataInfo.DataCenter;
        }

        public DataCenter GetData(string name)
        {
            if (string.IsNullOrEmpty(null))
            {
                throw new Exception(Utility.Text.Format("Param data name invaild."));
            }

            DataCenter dataCenter = null;
            foreach (var item in _dicDataInfos)
            {
                if (item.Value.DataCenter.Name == name)
                    dataCenter = item.Value.DataCenter;
            }

            return dataCenter;
        }

        public bool HasData<T>() where T : DataCenter
        {
            if (string.IsNullOrEmpty(null))
            {
                throw new Exception(Utility.Text.Format("Param data name invaild."));
            }

            Type type = typeof(T);

            if (_dicDataInfos != null)
                return _dicDataInfos.ContainsKey(type);

            return false;
        }

        public bool HasData(string name)
        {
            if (_dicDataInfos != null)
            {
                foreach (var item in _dicDataInfos)
                {
                    if (item.Value.DataCenter.Name == name)
                        return true;
                }
            }

            return false;
        }

        public void InitAllData()
        {
            if (_linkedListDataInfos == null)
                return;

            if (_linkedListDataInfos != null)
            {
                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                while (current != null)
                {
                    current.Value.DataCenter.Init();
                    current = current.Next;
                }
            }
        }

        public void PreLoadAllData()
        {
            if (_linkedListDataInfos != null)
            {
                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                while (current != null)
                {
                    current.Value.DataCenter.Preload();
                    current = current.Next;
                }
            }
        }

        public void LoadAllData()
        {
            if (_linkedListDataInfos != null)
            {
                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                while (current != null)
                {
                    current.Value.DataCenter.Load();
                    current = current.Next;
                }
            }
        }

        public void UnLoadAllData()
        {
            if (_linkedListDataInfos != null)
            {
                LinkedListNode<DataInfo> current = _linkedListDataInfos.First;
                while (current != null)
                {
                    current.Value.DataCenter.Unload();
                    current = current.Next;
                }
            }
        }
    }
}
